#include "XMLAudioData.h"
#include "CoreSystem.h"
#include "XMLDoc.h"
#include "Variable.h"
#include "Graph.h"
#include <fstream>
#include "../Libraries/String/String.h"

// XML TAGS
#define	SOUND_XML_TAG	"SoundXML"
#define SOUND_CATEGORY	"SoundCategory"
#define SOUND_PATH		"SoundPath"
#define	SOUND_FILE		"SoundFile"
#define SOUND_VARIABLE	"Variable"
#define	SOUND_GRAPH		"Graph"
#define SOUND_POINT		"Point"
#define SOUND_EVENT		"Event"
#define SOUND_SUBEVENT	"SubEvent"
#define SOUND_EFFECT	"Effect"

namespace Audio
{
	VariableHandle XMLAudioData::NewVariable(const char* name, float min, float max)
	///////////////////////////////////////////////////////////////////////////////
	// Description: 
	//
	// Recent Changes:
	// -brasmuss 4/11/2007 : Created
	///////////////////////////////////////////////////////////////////////////////
	{
		// Create
		VariableHandle handle = UniqueVarHandle();
		std::string sname(name);
		Variable* variable = new Variable(sname,min,max,max,handle);

		// Store
		m_varPool.insert(variablepair_t(handle,variable));
		m_varHandles.insert(handlepair_t(std::string(name),handle));
		return handle;
	}
	void XMLAudioData::NewAudioFile(const char* name, const char* file, FileHandle handle )
	{
		// Create
		AudioFile* audioFile = new AudioFile(name,file);		

		// Store
		m_audioFilePool.insert(audiofilepair_t(handle,audioFile));
		m_audioFileHandles.insert(handlepair_t(file,handle));
	}

	GraphHandle XMLAudioData::NewGraph(const char* name)
	///////////////////////////////////////////////////////////////////////////////
	// Description: 
	//
	// Recent Changes:
	// -brasmuss 4/11/2007 : Created
	///////////////////////////////////////////////////////////////////////////////
	{
		// Create
		GraphHandle handle = UniqueGraphHandle();
		Graph* graph = new Graph(name);

		// Store
		m_graphPool.insert(graphpair_t(handle,graph));
		m_graphHandles.insert(handlepair_t(std::string(name),handle));
		return handle;
	}

	EventHandle XMLAudioData::NewEvent(const char* name, bool looping)
	///////////////////////////////////////////////////////////////////////////////
	// Description: 
	//
	// Recent Changes:
	// -brasmuss 4/11/2007 : Created
	///////////////////////////////////////////////////////////////////////////////
	{
		// Create
		EventHandle handle = UniqueEventHandle();
		EventData* event = new EventData(std::string(name), looping);

		// Store
		m_eventPool.insert(eventdatapair_t(handle,event));
		m_eventHandles.insert(handlepair_t(std::string(name),handle));
		return handle;
	}

	Variable* XMLAudioData::GetVariable(const VariableHandle& handle)
	{
		variablemap_t::iterator iter = m_varPool.find(handle);
		if(iter == m_varPool.end())
			return NULL;
		return iter->second;
	}

	AudioFile* XMLAudioData::GetAudioFile(const FileHandle& handle)
	{
		audiofilemap_t::iterator iter = m_audioFilePool.find(handle);
		if(iter == m_audioFilePool.end())
			return NULL;
		return iter->second;
	}

	Graph* XMLAudioData::GetGraph(const GraphHandle& handle)
	{
		graphmap_t::iterator iter = m_graphPool.find(handle);
		if(iter == m_graphPool.end())
			return NULL;
		return iter->second;
	}

	EventData* XMLAudioData::GetEventData(const EventHandle& handle)
	{
		eventdatamap_t::iterator iter = m_eventPool.find(handle);
		if(iter == m_eventPool.end())
			return NULL;
		return iter->second;
	}

	VariableHandle XMLAudioData::GetVariableHandle(const char* name)
	{
		handlemap_t::iterator iter = m_varHandles.find(std::string(name));
		if(iter == m_varHandles.end())
			return 0;
		return iter->second;
	}

	VariableHandle XMLAudioData::GetVariableHandle(bool first)
	{
		if( first )
			m_curVarHandle = m_varHandles.begin();
		if( m_curVarHandle == m_varHandles.end() )
			return 0;
		return (VariableHandle)(m_curVarHandle++)->second;

	}
	FileHandle XMLAudioData::GetAudioFileHandle(const char* name)
	{
		handlemap_t::iterator iter = m_audioFileHandles.find(std::string(name));
		if(iter == m_audioFileHandles.end())
			return 0;
		return iter->second;
	}

	FileHandle XMLAudioData::GetAudioFileHandle(bool first)
	{
		if( first )
			m_curAudioFileHandle = m_audioFileHandles.begin();
		if( m_curAudioFileHandle == m_audioFileHandles.end() )
			return 0;

		return (FileHandle)(m_curAudioFileHandle++)->second;	
	}
	GraphHandle XMLAudioData::GetGraphHandle(const char* name)
	{
		handlemap_t::iterator iter = m_graphHandles.find(std::string(name));
		if(iter == m_graphHandles.end())
			return 0;
		return iter->second;
	}

	GraphHandle XMLAudioData::GetGraphHandle(bool first)
	{
		if( first )
			m_curGraphHandle = m_graphHandles.begin();
		if( m_curGraphHandle == m_graphHandles.end() )
			return 0;

		return (GraphHandle)(m_curGraphHandle++)->second;
	}
	EventHandle XMLAudioData::GetEventHandle(const char* name)
	{
		handlemap_t::iterator iter = m_eventHandles.find(std::string(name));
		if(iter == m_eventHandles.end())
			return 0;
		return iter->second;
	}

	EventHandle XMLAudioData::GetEventHandle(bool first)
	{
		if( first )
			m_curEventHandle = m_eventHandles.begin();
		if( m_curEventHandle == m_eventHandles.end() )
			return 0;

		return (EventHandle)(m_curEventHandle++)->second;
	}
	SubEventHandle XMLAudioData::GetSubEventHandle(	EventHandle eventID, bool first )
	{
		EventData* data = GetEventData(eventID);
		if( !data )
			return 0;

		if( first )
		{
			m_curSubEventHandle = data->m_subEvents.begin();
		}
		if( m_curSubEventHandle == data->m_subEvents.end() )
			return 0;

		return (SubEventHandle)(m_curSubEventHandle++)->first;		
	}
	EffectHandle XMLAudioData::GetEffectHandle(	EventHandle eventID, SubEventHandle subID, bool first )
	{
		EventData* data = GetEventData(eventID);
		if( !data )
			return 0;

		subeventdatamap_t::iterator itt = data->m_subEvents.find( subID );
		if( itt == data->m_subEvents.end() )
			return 0;

		if( first )
		{
			m_curEffectHandle = itt->second->m_effects.begin();
		}
		if( m_curEffectHandle == itt->second->m_effects.end() )
			return 0;

		return (EffectHandle)(m_curEffectHandle++)->first;		
	}
/*	SubEventHandle XMLAudioData::GetSubEventHandle(bool first)
	{
		
	}*/

	bool XMLAudioData::RemoveVariable(const VariableHandle& handle)
	{
		variablemap_t::iterator iter = m_varPool.find(handle);
		if(iter == m_varPool.end())
			return false;
		
		delete iter->second;
		m_varPool.erase(iter);
		return true;
	}

	bool XMLAudioData::RemoveGraph(const GraphHandle& handle)
	{
		graphmap_t::iterator iter = m_graphPool.find(handle);
		if(iter == m_graphPool.end())
			return false;

		delete iter->second;
		m_graphPool.erase(iter);
		return true;
	}

	bool XMLAudioData::RemoveAudioFile(const FileHandle& handle)
	{
		audiofilemap_t::iterator iter = m_audioFilePool.find(handle);
		if(iter == m_audioFilePool.end())
			return false;

		delete iter->second;
		m_audioFilePool.erase(iter);
		return true;
	}

	bool XMLAudioData::RemoveEventData(const EventHandle& handle)
	{
		eventdatamap_t::iterator iter = m_eventPool.find(handle);
		if(iter == m_eventPool.end())
			return false;

		delete iter->second;
		m_eventPool.erase(iter);
		return true;
	}

	void XMLAudioData::Clear()
	{
		//@todo memory leaks
		m_audioFileHandles.clear();
		m_varHandles.clear();	
		m_graphHandles.clear();
		m_eventHandles.clear();
		m_graphPool.clear();
		m_varPool.clear();		
		m_eventPool.clear();	
		m_audioFilePool.clear();
	}

	bool XMLAudioData::LoadSoundXML(const char* file, CoreSystem* core)
	///////////////////////////////////////////////////////////////////////////////
	// Description: Reads an XML file and loads all data types into memory
	//
	// Recent Changes:
	// -jkern 4/10/2007 : Created
	///////////////////////////////////////////////////////////////////////////////
	{
		std::ifstream checkExist;
		checkExist.open(file);
		if( checkExist.is_open() )
		{
			Clear();

			// Open and parse the file and get access to the tree
			XMLDoc xmlFile( SOUND_XML_TAG );
			xmlFile.Parse( file );
			XMLTree tree = xmlFile.GetTree();

			// Load the variables
			xmlrange_t range = tree.Find( SOUND_VARIABLE );
			for(; range.first != range.second; ++range.first )
			{
				std::string		name	= XML_NPTR(range.first)->GetAttribute<std::string>("name", "");
				float			min		= XML_NPTR(range.first)->GetAttribute<float>("min", 0.0f);
				float			max		= XML_NPTR(range.first)->GetAttribute<float>("max", 1.0f);
				core->NewVariable( name.c_str(), min, max, m_myHandle );
			}

			// Load the graphs
			range = tree.Find( SOUND_GRAPH );
			for(; range.first != range.second; ++range.first )
			{
				std::string		name	= XML_NPTR(range.first)->GetAttribute<std::string>("name", "");
				GraphHandle gID = NewGraph( name.c_str() );

				// Load all of the points in the graph
				XMLTree		subNodes(range.first);
				xmlrange_t	subRange = subNodes.Find( SOUND_POINT );
				for(; subRange.first != subRange.second; ++subRange.first )
				{
					float x = XML_NPTR(subRange.first)->GetAttribute<float>("x",0.0f);
					float y = XML_NPTR(subRange.first)->GetAttribute<float>("y",0.0f);
					core->AddPoint( gID, x,y, m_myHandle );
				}
			}

			range = tree.Find( SOUND_FILE );
			for(; range.first != range.second; ++range.first )
			{
				std::string name = XML_NPTR(range.first)->GetAttribute<std::string>("name","");
				std::string file = XML_NPTR(range.first)->GetAttribute<std::string>("filename","");
				FileHandle fHandle;

				fHandle = core->NewSoundFile( name.c_str(), file.c_str(), m_myHandle );
				//m_audioFileHandles.insert( handlepair_t(file, fHandle) );
				//core->NewSoundFile( name.c_str(), file.c_str(), fHandle );
			}

			// Load the events
			range = tree.Find( SOUND_EVENT );
			for(; range.first != range.second; ++range.first )
			{
				// Get the event name
				std::string		name	= XML_NPTR(range.first)->GetAttribute<std::string>("name", "");
				std::string		loop	= XML_NPTR(range.first)->GetAttribute<std::string>("looping", "false");
				bool			looping	= (loop == "true")? true : false;
				XMLTree			subNodes(range.first);

				// Create the event 
				EventHandle		e = NewEvent( name.c_str(), looping );

				xmlrange_t		subRange = subNodes.Find(SOUND_SUBEVENT);
				for(; subRange.first != subRange.second; ++subRange.first )
				{
					XMLTree		subNodes(subRange.first);

					// Load the attributes
					float		triggerTime	= XML_NPTR( subRange.first )->GetAttribute<float>("triggerTime",0.0f);
					std::string	soundName	= XML_NPTR( subRange.first )->GetAttribute<std::string>("soundName","");
					int			pitchShift  = XML_NPTR( subRange.first )->GetAttribute<int>("pitchShift",0);
					std::string	stream		= XML_NPTR( subRange.first )->GetAttribute<std::string>("streaming","false");
					bool		streaming	= (stream == "true")? true : false;

					// Create the sub event
					SubEventHandle se = core->AddSubEvent(e, soundName.c_str(), triggerTime, pitchShift, streaming, m_myHandle );

					xmlrange_t effectRange = subNodes.Find(SOUND_EFFECT);
					for(; effectRange.first != effectRange.second; ++effectRange.first )
					{
						// Load the attributes
						std::string graphName = XML_NPTR( effectRange.first )->GetAttribute<std::string>("graphName","");
						std::string varName   = XML_NPTR( effectRange.first )->GetAttribute<std::string>("varName","");
						unsigned	effect	  = XML_NPTR( effectRange.first )->GetAttribute<unsigned>("effect",0);

						// Create the effect
						core->AddSoundEffect(e, se, graphName.c_str(), varName.c_str(), effect, m_myHandle );
					}
				}
			}
			return true;
		}
		return false;
	}

	bool XMLAudioData::SaveSoundXML( const char* file )
	///////////////////////////////////////////////////////////////////////////////
	// Description: 
	//
	// Recent Changes:
	// -jkern 4/10/2007 : Created
	///////////////////////////////////////////////////////////////////////////////
	{
		xmlnodeptr_t	baseNode(new XMLNode( SOUND_XML_TAG ));

		// Add the variables to the tree
		variablemap_t::iterator var_iter;
		for(var_iter = m_varPool.begin(); var_iter != m_varPool.end(); ++var_iter)
		{
			Variable* pVar = var_iter->second;
			xmlnodeptr_t	variable( new XMLNode( SOUND_VARIABLE ) );

			xmlattrib_t		attName("name", pVar->m_name.c_str() );
			xmlattrib_t		attMin("min", String(pVar->m_min).cstr() );
			xmlattrib_t		attMax("max", String(pVar->m_max).cstr() );

			variable->AddAttribute( attName );
			variable->AddAttribute( attMin );
			variable->AddAttribute( attMax );

			baseNode->Link(variable);
		}

		// Save out the graphs
		graphmap_t::iterator graph_iter;
		for(graph_iter = m_graphPool.begin(); graph_iter != m_graphPool.end(); ++graph_iter)
		{
			Graph* pGraph = graph_iter->second;
			xmlnodeptr_t	graph( new XMLNode( SOUND_GRAPH ) );

			xmlattrib_t		attName( "name", pGraph->m_name.c_str() );
			graph->AddAttribute( attName );

			for( unsigned k=0; k < pGraph->m_points.size(); ++k )
			{
				Point& p = pGraph->m_points[k];

				xmlnodeptr_t	point( new XMLNode( SOUND_POINT ) );
				xmlattrib_t		attX( "x", String(p.m_x).cstr() );
				xmlattrib_t		attY( "y", String(p.m_y).cstr() );
				point->AddAttribute( attY );
				point->AddAttribute( attX );

				// Add the point to the graph
				graph->Link( point );
			}
			// Add the graph to the base node
			baseNode->Link( graph );
		}

			// Add all of the sound file nodes to the category
		audiofilemap_t::iterator	audio_iter;
		for(audio_iter = m_audioFilePool.begin(); audio_iter != m_audioFilePool.end(); ++audio_iter) 
		{
			AudioFile* pAudioFile = audio_iter->second;

			xmlnodeptr_t	soundFile( new XMLNode(SOUND_FILE) );
			xmlattrib_t		attName( "name", pAudioFile->m_name.c_str() );
			xmlattrib_t		attFile( "filename", pAudioFile->m_filename.c_str() );
			soundFile->AddAttribute( attFile );
			soundFile->AddAttribute( attName );

			// Add the sound file to the category
			baseNode->Link(soundFile);
		}


		// Save out the event information
		eventdatamap_t::iterator event_iter;
		for(event_iter = m_eventPool.begin(); event_iter != m_eventPool.end(); ++event_iter)
		{
			EventData* pEvent = event_iter->second;

			// Create a new event node and set it's name
			xmlnodeptr_t	eventNode( new XMLNode(SOUND_EVENT) );
			xmlattrib_t		attName( "name", pEvent->m_name.c_str() );
			xmlattrib_t		attLooping( "looping", ( pEvent->m_looping )? "true" : "false"  );
			eventNode->AddAttribute( attName );
			eventNode->AddAttribute( attLooping );

			// Add all of the sub event nodes to the event
			subeventdatamap_t::iterator subevent_iter = pEvent->m_subEvents.begin();
			for(; subevent_iter != pEvent->m_subEvents.end(); ++subevent_iter )
			{
				SubEventData* pSub = subevent_iter->second;

				// Create a new sub event node
				xmlnodeptr_t	subEventNode( new XMLNode(SOUND_SUBEVENT) );

				xmlattrib_t		attTrigger( "triggerTime", String(pSub->m_trigger).cstr() );
				xmlattrib_t		attSoundName( "soundName", pSub->m_soundName.c_str() );
				String			pitchShift( pSub->m_pitchShift );
				xmlattrib_t		attPitchShift( "pitchShift", pitchShift.cstr() );
				xmlattrib_t		attStreaming( "streaming", (pSub->m_streaming)? "true" : "false" );

				subEventNode->AddAttribute( attTrigger );
				subEventNode->AddAttribute( attSoundName );
				subEventNode->AddAttribute( attPitchShift );
				subEventNode->AddAttribute( attStreaming );

				effectdatamap_t::iterator effect_iter = pSub->m_effects.begin();
				for( ; effect_iter != pSub->m_effects.end(); ++effect_iter )
				{
					EffectData* fx = effect_iter->second;

					// Create a new effect node
					xmlnodeptr_t	effectNode( new XMLNode(SOUND_EFFECT) );
					xmlattrib_t		attGraphName( "graphName", fx->m_graph.c_str() );
					xmlattrib_t		attVarName( "varName", fx->m_variable.c_str() );
					xmlattrib_t		attEffect( "effect", String(fx->m_type).cstr() );

					effectNode->AddAttribute(attGraphName);
					effectNode->AddAttribute(attVarName);
					effectNode->AddAttribute(attEffect);

					// Add the effect node to the sub event node
					subEventNode->Link(effectNode);
				}

				// Add the sub event node to the event node
				eventNode->Link(subEventNode);
			}

			// Add the event node to the base node
			baseNode->Link( eventNode );
		}


		// Create the tree and set the base node
		xmltreeptr_t	tree( new XMLTree );
		tree->Link(baseNode);

		// Save out the xml file
		XMLDoc xmlFile( SOUND_XML_TAG );
		xmlFile.SetTree(tree);
		xmlFile.Write(file);

		return true;
	}
}
